#include <iostream>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cerrno>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>

void usage(std::string proc)
{
  std::cout << "usage:\n\t" << proc << " need port" << std::endl;
}

int main(int argc, char* argv[])
{
  if (argc != 2)
  {
    usage(argv[0]);
    return 0;
  }

  //1.创建套接字,打开网络文件
  int sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock < 0)
  {
    perror("create socket");
    std::cerr << "errno : " << errno << std::endl;
    return 1;
  }

  //2.给服务端绑定端口和IP
  const uint16_t port = atoi(argv[1]);//因为端口号的范围是0~65535,而16位刚好可以表示这么多的数
  struct sockaddr_in local;
  memset(&local, 0, sizeof(local));
  local.sin_family = AF_INET;
  local.sin_port = htons(port);//此处的端口号是计算机上的变量,是主机序列,需要转换成为网络序列
  //PS:在云服务器上,不允许bind公网IP,另外,我们实际正常编写的时候,也不会指明绑定的IP
  //如果需要绑定IP地址需要考虑下面两点:
  //1.需要将点分十进制,字符串风格的IP地址,转换成4字节整数IP
  //2.需要考虑机器大小端的问题
  //in_addr_t inet_addr(const char* cp);能够完成上面的工作
  //比如local.sin_addr.s_add = inet_addr("192.168.222.129");
  //但是我们一般都是采用下面这一种绑定方法
  //local.sin_addr.s_addr = INADDR_ANY;
  //网络地址设置为INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡
  //每个网卡也可能绑定了多个IP地址,这样设置可以在所有的IP地址进行数据的接收
  //如果bind的是确定的IP,意味着只有发到该IP上面的数据,才会交给你的网络进程
  //我们需要的是,所有发送到该主机,发送到该端口的数据
  local.sin_addr.s_addr = INADDR_ANY;

  if (bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
  {
    perror("bind");
    std::cerr << "errno : " << errno << std::endl;
    return 2;
  }

  //3.提供服务
  bool quit = false;
  #define NUM 1024
  char buffer[NUM] = { 0 };
  while(!quit)
  {
    struct sockaddr_in peer;
    memset(&peer, 0, sizeof(peer));
    socklen_t len = sizeof(peer);

    //注意:我们默认认为通信的数据是双方在互发字符串
    ssize_t ret = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);

    if (ret > 0)
    {
      buffer[ret] = '\0';
      //Linux中的popen()函数可以在程序中执行一个shell命令，并返回命令执行的结果
      //有两种操作模式，分别为读和写。
      //在读模式中，程序中可以读取到命令的输出
      //其中有一个应用就是获取网络接口的参数
      //在写模式中，最常用的是创建一个新的文件或开启其他服务等
      
      std::cout << "client# " << buffer << std::endl;
      FILE* fp = popen(buffer, "r");

      std::string echo;
      char line[NUM] = { 0 };
      //将popen的结果以此写入echo里面
      while(fgets(line, sizeof(line), fp) != nullptr)
      {
        echo += line;
      }
      
      sendto(sock, echo.c_str(), echo.size(), 0, (struct sockaddr*)&peer, len);
      pclose(fp);

    }

  }
  return 0;
}
